Module MCMC_c_bindings

  use iso_c_binding

  Implicit none

  interface allocate_gpu_mem
    subroutine allocate_gpu_mem_cuda(nmeasuremax,numsites,nmeasure,statesize,dim1,&
                            dim2,nmeasuretotal) bind(c)
      use iso_c_binding
      integer (C_INT), value :: nmeasuremax,numsites,nmeasure,statesize,dim1,&
                            dim2,nmeasuretotal
    end subroutine
  end interface

  interface 
    subroutine setup_gpu_cuda(pdf_param1_current,pdf_param2_current,x,p0_x,&
                       p0_pdf_param1,p0_pdf_param2,stepsize_pdf_param1,&
                       stepsize_pdf_param2,pdf_param1,pdf_param2,&
                       pdf_param1_pdf,pdf_param2_pdf,stepsize,pdf_hyperparam1,&
                       pdf_hyperparam2,H,x_pdf) bind(c)
      use iso_c_binding
      type(C_PTR), value :: pdf_param1_current,pdf_param2_current,x,p0_x,&
                       p0_pdf_param1,p0_pdf_param2,stepsize_pdf_param1,&
                       stepsize_pdf_param2,pdf_param1,pdf_param2,&
                       pdf_param1_pdf,pdf_param2_pdf,stepsize,pdf_hyperparam1,&
                       pdf_hyperparam2,H,x_pdf
    end subroutine
  end interface

  interface free_gpu_mem
    subroutine free_gpu_mem_cuda() bind(c)
    end subroutine
  end interface

  interface
    subroutine compute_x_gpu_cuda(n0T, n0, Tinv_current, Sinv_current,&
                                  burn_in_done) bind(c)
      use iso_c_binding
      real(C_FLOAT) :: n0T
      type(C_PTR), value :: n0, Tinv_current, Sinv_current
      logical(C_BOOL), value :: burn_in_done
    end subroutine
  end interface

  interface
    subroutine copy_data_back_cuda(pdf_param1_current, pdf_param2_current, x,&
                                   reject) bind(c)
      use iso_c_binding
      type(C_PTR), value :: pdf_param1_current, pdf_param2_current, x
      integer(C_INT) :: reject
    end subroutine
  end interface

  interface
    subroutine copy_final_data_back_cuda(reject_vector) bind(c)
      use iso_c_binding
      type(C_PTR), value :: reject_vector
    end subroutine
  end interface

Contains

  subroutine setup_gpu(statesize,pdf_param1_current,pdf_param2_current,x,p0_x,&
                       p0_pdf_param1,p0_pdf_param2,stepsize_pdf_param1,&
                       stepsize_pdf_param2,pdf_param1,pdf_param2,&
                       pdf_param1_pdf,pdf_param2_pdf,stepsize,pdf_hyperparam1,&
                       pdf_hyperparam2,H,x_pdf)
    integer, intent(in) :: statesize
    real, dimension(statesize), intent(in), target :: pdf_param1_current, &
       pdf_param2_current,x,p0_x, p0_pdf_param1,p0_pdf_param2, &
       stepsize_pdf_param1,stepsize_pdf_param2,pdf_param1,pdf_param2,&
       stepsize,pdf_hyperparam1,pdf_hyperparam2,H
    integer, dimension(statesize), intent(in), target :: pdf_param1_pdf,&
       pdf_param2_pdf,x_pdf

    call setup_gpu_cuda(C_LOC(pdf_param1_current),C_LOC(pdf_param2_current),&
                        C_LOC(x), C_LOC(p0_x), C_LOC(p0_pdf_param1), C_LOC(p0_pdf_param2),&
                        C_LOC(stepsize_pdf_param1),C_LOC(stepsize_pdf_param2),&
                        C_LOC(pdf_param1),C_LOC(pdf_param2),C_LOC(pdf_param1_pdf),&
                        C_LOC(pdf_param2_pdf),C_LOC(stepsize),C_LOC(pdf_hyperparam1),&
                        C_LOC(pdf_hyperparam2),C_LOC(H),C_LOC(x_pdf))
  end subroutine

  subroutine compute_x_gpu(nmeasuremax,nmeasuretotal,numsites,n0T,n0,&
                           Tinv_current,Sinv_current,burn_in_done)
    integer, intent(in) :: nmeasuremax,nmeasuretotal,numsites
    real, intent(inout) :: n0T
    real, dimension(nmeasuremax), intent(inout), target :: n0
    real, dimension(nmeasuretotal,nmeasuretotal),&
          intent(in), target :: Tinv_current
    real, dimension(numsites,numsites),& 
          intent(in), target :: Sinv_current
    logical, intent(in) :: burn_in_done

    call compute_x_gpu_cuda(n0T, C_LOC(n0), C_LOC(Tinv_current),&
                            C_LOC(Sinv_current), LOGICAL(burn_in_done, C_BOOL))
    
  end subroutine

  subroutine copy_data_back(statesize,pdf_param1_current,pdf_param2_current,x,reject)
    integer, intent(in) :: statesize
    real, dimension(statesize), intent(inout), target :: pdf_param1_current,&
                           pdf_param2_current, x
    integer, intent(inout) :: reject

    call copy_data_back_cuda(C_LOC(pdf_param1_current),C_LOC(pdf_param2_current),&
                             C_LOC(x), reject)
  end subroutine

  subroutine copy_final_data_back(statesize, reject_vector)
    integer, intent(in) :: statesize
    integer, dimension(statesize), intent(inout), target :: reject_vector

    call copy_final_data_back_cuda(C_LOC(reject_vector))
  end subroutine

end module MCMC_c_bindings

